// ignore_for_file: invalid_use_of_internal_member

import 'dart:async';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:safetymonitor/app/components/toast_widget.dart';
import 'package:safetymonitor/app/core/constans/error_code.dart';
import 'package:safetymonitor/app/core/http/network_manger.dart';
import 'package:safetymonitor/app/core/http/result.dart';
import 'package:safetymonitor/app/core/utils/log_util.dart';
import 'package:safetymonitor/app/core/utils/user_manager.dart';
import 'package:safetymonitor/generated/json/base/json_convert_content.dart';
import 'package:safetymonitor/router/app_pages.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';

export 'result.dart';
bool _isShowToast = true;
/// http 操作类
///
/// https://github.com/flutterchina/dio/blob/master/README-ZH.md
class HttpUtil {
  factory HttpUtil() => _instance;

  HttpUtil._internal({bool showToast = true, this.authToken,bool isPrinting = true}) {
    final options = BaseOptions(
      //连接服务器超时时间，单位是毫秒.
      connectTimeout: const Duration(seconds: 12),
      // 响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: const Duration(seconds: 12),
      sendTimeout: const Duration(seconds: 12),
      contentType: 'application/json',
      responseType: ResponseType.json,
    );

    dio = Dio(options);

    // 添加拦截器
    dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        final headers = {
          'content-type': 'application/json',
          'Authorization': 'Basic c3dhZ2dlcjoxMjM0NTY=',
          'tenant-id': '1',
        };
        final userToken = authToken ?? UserManager.getToken();
        if (userToken?.isBlank == false) {
          headers['Authorization'] = '$userToken';
          // headers['content-type'] = 'application/x-www-forcm-urlencoded';
        }
        options.headers = headers;
        return handler.next(options);
      },
      onResponse: (response, handler) {
        final data = response.data;
        // if(data['code'] == 401){
        //   // 提示
        //   AppToast.showError('登录失效,请重新登录');
        //   _goLoginPage();
        // }
        if (data == null || (data is Map && data['code'] != 0 && data['code'] != 200)) {
          return handler.reject(
            DioException(
              requestOptions: response.requestOptions,
              response: response,
            ),
            true,
          );
        }
        return handler.next(response);
      },
      onError: (DioException e, handler) {
        // Do something with response error
        final eInfo = _createErrorEntity(e);
        if (showToast) {
          if(eInfo.code == 401) {
            if(_isShowToast){
              _isShowToast = false;
              AppToast.showError('${eInfo.code}   ${eInfo.message}');
            }
          } else if(eInfo.code == 403&&eInfo.message=='没有该操作权限') {
            _isShowToast = true;
            AppToast.showError(eInfo.message);
          }else{
            _isShowToast = true;
            AppToast.showError('${eInfo.code}   ${eInfo.message}');
          }
        }
        switch (eInfo.code) {
          case 401: // 没有权限 重新登录
            _goLoginPage();
            break;
          default:
        }
        return handler.next(e);
        // 如果你想完成请求并返回一些自定义数据，可以resolve 一个`Response`,如`handler.resolve(response)`。
        // 这样请求将会被终止，上层then会被调用，then中返回的数据将是你的自定义response.
      },
    ));

    /// 开发服可以打印日志
    dio.interceptors.add(
      PrettyDioLogger(
        // 开发暂时不用打印请求头,token比较长
        requestHeader: true,
        requestBody: true,
        compact: true,
        responseBody: true,
      ),
    ); //开启请求日志

    cacheOptions = CacheOptions(
      // 数据缓存3天
      maxStale: const Duration(days: 3),
      hitCacheOnErrorExcept: [401, 403],
      store: HiveCacheStore(Directory.systemTemp.path),
      // BackupCacheStore(
      //   // 先缓存到内存
      //   primary: MemCacheStore(),
      //   // 再缓存到磁盘
      //   secondary: HiveCacheStore(Directory.systemTemp.path),
      // ),
      // 默认不开启缓存
      policy: CachePolicy.noCache,
    );
    dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));

  }

  final String? authToken;

  static final HttpUtil _instance = HttpUtil._internal();

  late final Dio dio;

  late final CacheOptions cacheOptions;

  /// 取消请求
  ///
  /// 同一个cancel token 可以用于多个请求，当一个cancel token取消时，所有使用该cancel token的请求都会被取消。
  void cancelRequests(CancelToken token) {
    if (!token.isCancelled) {
      token.cancel();
    }
  }

  /// token失效，去登录页
  void _goLoginPage() {
    if (UserManager.isLoggedIn) {
      UserManager.removeUserInfo();
      Get.offNamedUntil(
        Routes.authLogin,
        (route) => false,
      );
    }
  }

  /// 生成Get请求
  ///
  /// refreshCache 是否刷新缓存 默认 false, true表示忽略本地缓存, 先请求服务器, 再缓存数据
  /// enableCache 是否缓存 默认 false, true 表示优先采用本地缓存, 不存在或失效时, 才请求服务器, 再缓存数据
  HttpRequest<T> generateGetRequest<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    bool refreshCache = false,
    bool enableCache = false,
  }) {
    Options? options;

    if (refreshCache) {
      options = cacheOptions.copyWith(policy: !enableCache ? CachePolicy.refresh : CachePolicy.refreshForceCache,).toOptions();
    } else if (enableCache) {
      options = cacheOptions.copyWith(policy: CachePolicy.forceCache).toOptions();
    }
    return HttpRequest<T>(DioMixin.checkOptions('GET', options).compose(
      dio.options,
      path,
      queryParameters: queryParameters,
    ));
  }

  /// 生成Post请求
  HttpRequest<T> generatePostRequest<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    AppLogger.devLog(queryParameters.toString());
    //AppLogger.devLog(data.toString());
    return HttpRequest<T>(DioMixin.checkOptions('POST', options).compose(
      dio.options,
      path,
      data: data,
      queryParameters: queryParameters,
    ));
  }

  /// 生成Put请求
  HttpRequest<T> generatePutRequest<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    return HttpRequest<T>(DioMixin.checkOptions('PUT', options).compose(
      dio.options,
      path,
      data: data,
      queryParameters: queryParameters,
    ));
  }

  /// 生成Patch请求
  HttpRequest<T> generatePatchRequest<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    return HttpRequest<T>(DioMixin.checkOptions('PATCH', options).compose(
      dio.options,
      path,
      data: data,
      queryParameters: queryParameters,
    ));
  }

  /// 生成Delete请求
  HttpRequest<T> generateDeleteRequest<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    return HttpRequest<T>(DioMixin.checkOptions('DELETE', options).compose(
      dio.options,
      path,
      queryParameters: queryParameters,
    ));
  }
}

class HttpRequest<T> {
  HttpRequest(this.requestOptions);

  final RequestOptions requestOptions;

  /// 执行请求
  /// [isolate] 是否新开线程执行请求，适用于大数据量的请求
  /// [cancelToken] 取消请求的token，用于识别请求
  /// [isPrinting] 是否在控制台打印日志
  /// [isShowToast] 是否显示toast
  Future<Result<T>> execute({
    CancelToken? cancelToken,
    bool isolate = false,
    bool isShowLoading = false,
    bool isPrinting = true,
    bool isShowToast = true,
    String? showText
  }) async {
    if (cancelToken != null) {
      requestOptions.cancelToken = cancelToken;
    }
    // 加载框
    if (isShowLoading) {
      SmartDialog.showLoading(backType: SmartBackType.block,msg: showText ?? '加载中...');
    }
    if (isolate) {
      try {
        final resp = await compute(
          HttpUtil._internal(
            authToken: UserManager.getToken(),
            showToast: false,
          ).dio.fetch,
          requestOptions,
        );
        final result = await compute(_decodeData<T>, resp.data['data']);
        // 关闭加载框
        if (isShowLoading) {
          SmartDialog.dismiss();
        }
        if (result.isFailed) {
          return await compute(_decodeData<T>, result);
        }
        return result;
      } catch (e) {
        // 关闭加载框
        if (isShowLoading) {
          SmartDialog.dismiss();
        }
        return await compute(_decodeError<T>, e);
      }
    } else {
      try {
        final resp = await HttpUtil._internal(
          isPrinting: isPrinting,
          showToast: isShowToast,
        ).dio.fetch(requestOptions);
        final result = _decodeData<T>(resp.data['data']);
        // 关闭加载框
        if (isShowLoading) {
          SmartDialog.dismiss();
        }
        if (result.isFailed) {
          return _decodeData<T>(result);
        }
        return result;
      } catch (e) {
        // 关闭加载框
        if (isShowLoading) {
          SmartDialog.dismiss();
        }
        return _decodeError<T>(e);
      }
    }
  }
}

Result<T> _decodeData<T>(dynamic data) {
  Pagination? pagination;
  T? res;

  if (data is Map && Pagination.isPaginationStructure(data)) {
    pagination = JsonConvert.fromJsonAsT<Pagination>(data);
    data = data['list'];
  }

  final type = T.toString();
  if (data == null) {
    res = null;
  } else if (type == (int).toString() ||
      type == (String).toString() ||
      type == (double).toString() ||
      type == (bool).toString() ||
      type == dynamic.toString()) {
    res = data as T;
  } else if (data is List && (<int>[] is T || <String>[] is T || <double>[] is T || <bool>[] is T || <dynamic>[] is T)) {
    if (<int>[] is T) {
      res = data.map<int>((dynamic e) => e as int).toList() as T;
    } else if (<String>[] is T) {
      res = data.map<String>((dynamic e) => e as String).toList() as T;
    } else if (<double>[] is T) {
      res = data.map<double>((dynamic e) => e as double).toList() as T;
    } else if (<bool>[] is T) {
      res = data.map<bool>((dynamic e) => e as bool).toList() as T;
    } else if (<dynamic>[] is T) {
      res = data as T;
    }
  } else {
    try {
      res = JsonConvert.fromJsonAsT<T>(data);
    } catch (e) {
      return Result<T>.fail(-1, '解析数据发生错误');
    }
  }
  return Result<T>.success(res, pagination: pagination);
}

Result<T> _decodeError<T>(dynamic error) {
  if (error is DioException) {
    final data = error.response?.data;
    if (data is Map) {
      int code;
      String message;
      if (data.containsKey('code')) {
        if (data['code'] != null) {
          if (data['code'] is String) {
            code = int.tryParse(data['code'].toString()) ?? ErrorCodeConstants.unknown;
          } else {
            code = data['code'] as int;
          }
        } else {
          code = ErrorCodeConstants.unknown;
        }
        message = data['msg']?.toString() ?? '未知错误';
        return Result<T>.fail(code, message, errorData: data['data']);
      }
    }
    final eInfo = _createErrorEntity(error);
    return Result<T>.fail(eInfo.code, eInfo.message);
  } else {
    return Result.fail(ErrorCodeConstants.unknown, '未知错误');
  }
}

// error统一处理 错误信息
_ErrorEntity _createErrorEntity(DioException error) {
  switch (error.type) {
    case DioExceptionType.cancel:
      // 取消请求不弹toast了。 -2 表示请求取消.
      return _ErrorEntity(code: ErrorCodeConstants.cancel, message: '请求被取消');
    case DioExceptionType.connectionTimeout:
      NetworkManager.checkNetworkType();
      return _ErrorEntity(code: ErrorCodeConstants.connectTimeout, message: '因切换网络,请重新登录');
    case DioExceptionType.sendTimeout:
      return _ErrorEntity(code: ErrorCodeConstants.sendTimeout, message: '请求失败,请尝试重新登录');
    case DioExceptionType.connectionError:
      return _ErrorEntity(code: ErrorCodeConstants.receiveTimeout, message: '连接错误');
    case DioExceptionType.receiveTimeout:
      return _ErrorEntity(code: ErrorCodeConstants.receiveTimeout, message: '响应超时');
    case DioExceptionType.badResponse:
      {
        try {
          if (error.response?.data is Map && error.response?.data['code'] != null) {
            final Map<String, dynamic> data = error.response?.data as Map<String, dynamic>;
            final int code = data['code'] as int;
            final String message = data['msg'].toString();
            return _ErrorEntity(code: code, message: message);
          }
          final int? errCode = error.response?.statusCode;
        return _errStr(errCode,error.message.toString());
        } on Exception catch (_) {
          return _ErrorEntity(code: ErrorCodeConstants.unknown, message: '未知错误');
        }
      }
    default: {
      if (error.response?.data is Map && error.response?.data['code'] != null) {
        final Map<String, dynamic> data = error.response?.data as Map<String, dynamic>;
        final int code = data['code'] as int;
        final String message = data['msg'].toString();
        return _ErrorEntity(code: code, message: message);
        // return _errStr(code,message);
      }
          return _ErrorEntity(code: error.response != null ? error.response!.statusCode! : ErrorCodeConstants.unknown, message: error.message.toString());
        }
  }
}

_ErrorEntity _errStr(int? errCode, String? error) {
  switch (errCode) {
  case 400:
  return _ErrorEntity(code: errCode!, message: '请求语法错误');
  case 401:
  return _ErrorEntity(code: errCode!, message: '登录失效请重新登录');
  case 403:
  return _ErrorEntity(code: errCode!, message: '服务器拒绝执行');
  case 404:
  return _ErrorEntity(code: errCode!, message: '无法连接服务器');
  case 405:
  return _ErrorEntity(code: errCode!, message: '请求方法被禁止');
  case 500:
  return _ErrorEntity(code: errCode!, message: '服务器内部错误');
  case 502:
  return _ErrorEntity(code: errCode!, message: '无效的请求');
  case 503:
  return _ErrorEntity(code: errCode!, message: '服务器故障');
  case 505:
  return _ErrorEntity(code: errCode!, message: '不支持HTTP协议请求');
  case 1100:
  return _ErrorEntity(code: errCode!, message:error.toString());
  default:
  // return _ErrorEntity(
  // code: errCode ?? ErrorCodeConstants.unknown,
  // message: error ?? '未知错误',
  // );
      return _ErrorEntity(code: errCode!, message: error.toString());
  }
}

// 异常处理
class _ErrorEntity implements Exception {
  _ErrorEntity({required this.code, required this.message});

  int code;
  String message;

  @override
  String toString() {
    return 'Exception: code $code, $message';
  }
}
